package abstractGA;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.UUID;
import algorithm.Alg;
import algorithm.AlgCtx;
import foundation.concurrency.Product;
import abstractGA.chrom.Chrom;
import abstractGA.popu.PopuBuilder;
import abstractGA.popu.Population;
import abstractGA.result.OutData;

/**
 * 抽象的遗传算法
 * 
 * @author nyc
 *
 */
public class AbsGAAlg extends Alg {
	// 种群构建器
	protected PopuBuilder popuBld = null;
	// 当前种群
	protected Population curPopu = null;
	// 算法参数
	protected GAParameter parameter = null;
	// 数据输出类
	protected OutData outData = null;
	//判断算法一次运行完后，插入数据库是否完成
	protected boolean isFinished = false;
	/**
	 * 构造函数
	 * 
	 * @param popuBld：种群构造器
	 */
	public AbsGAAlg(PopuBuilder popuBld) {
		this.popuBld = popuBld;
	}

	/**
	 * 算法运行初始化
	 * 
	 * @param algCtx：算法运行上下文
	 */
	public void init(AlgCtx algCtx) {
		// new GAParameter(algCtx.getAlgCfgFileName());
		popuBld.buildPara(algCtx);
		popuBld.buildRandom();
		popuBld.buildChroms(algCtx);
		curPopu = popuBld.getPopu();
		parameter = curPopu.getParameter();
		parameter.setNeedOutPopulation(false);
		outData = new OutData(parameter);
	}

	@Override
	public void run(AlgCtx algCtx, int runNum, String rltFlNm) {
		//记录一次运行开始时间
		SimpleDateFormat  startDate = new SimpleDateFormat("yyyy-MM-dd_kk-mm-ss");
		String startTime= startDate.format(new Date());
		
		ArrayList<String> runIDs = new ArrayList<String>();
		//算法结果输出到excel文件的线程
		OutGAResultThread algOutThrd = null;//
		for (int i = 1; i <= runNum; i++) {
			init(algCtx);// 初始化算法
			int maxItem = parameter.getMaxIterNum();// 一次运行最多演化代数
			
			//演化产生的种群数据需要送数据库，已考虑并发读写
			Product<PopuData> prod = new Product<PopuData>();
			prod.setMaxNum(maxItem);
			OutPopuThread consummer = new OutPopuThread(prod, algCtx, outData);
			consummer.start();
			
			UUID uuid = UUID.randomUUID();// GUID
			String runID = algCtx.getAlgName() + uuid.toString();
			runIDs.add(runID);
			System.out.println("算法第" + i + "次开始运行.......");
			int genNum = 1;// 运行代数
			Chrom bestChrom = curPopu.getBestChroms().get(0).copy();
			while (genNum <= maxItem) {
				// 选择操作,产生临时种群
				Population tempPopu = curPopu.mutingChoose();
				// 进行单点交叉操作
//				tempPopu.crossover(1);
				// 进行单点变异操作
				tempPopu.mutate(1);
				// 全局最优目标值
				float bestFitness = bestChrom.getObjVals().get(0).floatValue();
				// 获取临时种群中最优染色体
				Chrom curBestChrom = tempPopu.getBestChroms().get(0);
				float curFitness = curBestChrom.getObjVals().get(0).floatValue();
				if (Float.compare(bestFitness, curFitness) > 0) {// 更新最优染色体
					bestChrom = curBestChrom.copy();
					bestFitness = curFitness;
				}
				// 通过环境选择获取下一代种群，环境选择后最优染色体将是两代最优染色体中的最优者且对应于下标为0的染色体
				Population nxtPopu = curPopu.envSelect(tempPopu);
				// 装配算法当前代最优解对应的结果
				System.out.println(bestChrom.outChrom());
				curPopu = nxtPopu;
				// 输出种群到数据库
				PopuData data = new PopuData();
				data.setRunID(runID);
				data.setGenID(String.valueOf(genNum));
				data.setCurPopu(curPopu.copy());// 避免种群被修改
				prod.addData(data);
				genNum++;

			}
			//等待算法运行一次后插入数据库完成
			while(!isFinished){
				isFinished=consummer.isFinish();
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("本次运行" + genNum + "代，最优解是：" + bestChrom.outChrom());
		}
		//记录一次运行结束时间
		SimpleDateFormat endDate = new SimpleDateFormat("yyyy-MM-dd_kk-mm-ss");
		String endTime = endDate.format(new Date());
		// 拼凑.xls格式
		rltFlNm = rltFlNm + "_startTime=" + startTime + "_endTime=" + endTime + ".xls";
		// 算法结果输出到excel文件
		algOutThrd = new OutGAResultThread(outData, parameter, rltFlNm, runIDs);
		algOutThrd.outToExcel(startTime, endTime);
//		algOutThrd.start();//后台运行
//		try {
//			algOutThrd.join(0);//参数为0表示等待algOutThrd线程一直运行结束,
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}

		outData.closeDBConn();// 关闭数据库连接
	}

	public PopuBuilder getPopuBld() {
		return popuBld;
	}

	public void setPopuBld(PopuBuilder popuBld) {
		this.popuBld = popuBld;
	}

	public Population getCurPopu() {
		return curPopu;
	}

	public void setCurPopu(Population curPopu) {
		this.curPopu = curPopu;
	}

	public GAParameter getParameter() {
		return parameter;
	}

	public void setParameter(GAParameter parameter) {
		this.parameter = parameter;
	}

}
